package fr.utbm.gi.vi51.project.agent;

import java.util.ArrayList;
import java.util.List;

import javax.vecmath.Point2d;

import org.janusproject.kernel.status.Status;

import fr.utbm.gi.vi51.framework.environment.Animat;
import fr.utbm.gi.vi51.framework.environment.Environment;
import fr.utbm.gi.vi51.framework.environment.Perception;
import fr.utbm.gi.vi51.project.agent.body.EnemyBody;
import fr.utbm.gi.vi51.project.agent.body.MarineBody;
import fr.utbm.gi.vi51.project.agent.body.UnitBody;
import fr.utbm.gi.vi51.project.behavior.BehaviourOutput;
import fr.utbm.gi.vi51.project.behavior.SeekBehaviour;
import fr.utbm.gi.vi51.project.behavior.steering.SteeringSeekBehaviour;
import fr.utbm.gi.vi51.project.environment.EnvironmentObject;
import fr.utbm.gi.vi51.project.environment.EnvironmentType;
import fr.utbm.gi.vi51.project.environment.WorldModel;
import fr.utbm.gi.vi51.project.formations.FormationBody;

public class Marine extends Animat<MarineBody> {

	private static final long serialVersionUID = -7917250901907052078L;

	// Behaviors
	protected final SeekBehaviour<?> seekBehaviour;

	// Formation
	public static FormationBody formationBody;
	private int formationPosition;
	// Attack
	private int cooldown = 0;

	public Marine(FormationBody formationBody, int formationPosition) {
		Marine.formationBody = formationBody;
		this.formationPosition = formationPosition;
		this.seekBehaviour = new SteeringSeekBehaviour();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected MarineBody createBody(Environment in) {
		return new MarineBody(getAddress());
	}

	@Override
	public Status live() {
		// Check if marine is dead
		if (((UnitBody) getBody()).getHealth() <= 0) {
			WorldModel.removeMarine(this);
			this.killMe();
		} else {
			List<Perception> perceivedObjects = getPerceivedObjects();
			// List of perceived obstacles and enemies
			List<Point2d> perceivedObstaclesPositions = new ArrayList<>();
			List<UnitBody> perceivedEnemies = new ArrayList<>();
			for (Perception p : perceivedObjects) {
				if (p.getPerceivedObject() instanceof EnvironmentObject
						&& ((EnvironmentObject) p.getPerceivedObject()).getType() != EnvironmentType.TARGET) {
					perceivedObstaclesPositions.add(p.getPerceivedObject().getPosition());
				} else if (p.getPerceivedObject() instanceof EnemyBody) {
					perceivedEnemies.add((UnitBody) p.getPerceivedObject());
				}
			}

			// if the agent see an enemy, he shoots the nearest one !
			if (perceivedEnemies.size() > 0 && this.cooldown == 0) {
				UnitBody nearestEnemy = getNearestEnemy(perceivedEnemies);
				if (getBody().getFrustrum().isAttackable(getPosition(), nearestEnemy.getPosition())
						&& this.cooldown <= 0) {
					attack(nearestEnemy);
				}
			}

			// return to normal icon
			if (this.cooldown > 0 && this.cooldown < getBody().getAttackAnimationEnd()) {
				getBody().setCurrentIcon(getBody().getNormalIcon());
			}

			// Go to formation target location
			Point2d position = Marine.formationBody.getSlotAt(this.formationPosition).getGlobalPosition();
			BehaviourOutput behaviour = this.seekBehaviour.runSeek(getPosition(), getDirection(),
					getCurrentLinearSpeed(), getMaxLinearSpeed(), position, perceivedObstaclesPositions);
			// Check if marine is going to wait
			influenceKinematic(behaviour.getLinear(), behaviour.getAngular());
			if (this.cooldown > 0) {
				this.cooldown--;
			}
		}
		return null;
	}

	// return the nearest Enemy
	protected UnitBody getNearestEnemy(List<UnitBody> perceivedEnemies) {
		int rangMin = 0, rang = 0;
		double minDistance, distance;
		minDistance = Math.sqrt(Math.pow((perceivedEnemies.get(0).getPosition().x - this.getPosition().x), 2)
				+ Math.pow((perceivedEnemies.get(0).getPosition().y - this.getPosition().y), 2));
		for (UnitBody unitBody : perceivedEnemies) {
			distance = Math.sqrt(Math.pow((unitBody.getPosition().x - this.getPosition().x), 2)
					+ Math.pow((unitBody.getPosition().y - this.getPosition().y), 2));
			if (distance < minDistance)
				rangMin = rang;
			rang++;
		}
		return perceivedEnemies.get(rangMin);
	}

	protected void attack(UnitBody unitBody) {
		// if the armor's prey is greatest than the shooter damage then nothing happens
		// else the prey damage equal to the damage minus armor
		if (this.getBody().getDamage() > unitBody.getArmor()) {
			unitBody.setHealth(unitBody.getHealth() - this.getBody().getDamage() + unitBody.getArmor());
			getBody().setCurrentIcon(getBody().getAttackIcon());
			this.cooldown = getBody().getAttackCooldown();
		}
	}

	public static void setFormationBody(FormationBody formationBody) {
		Marine.formationBody = formationBody;
	}

	public void setFormationPosition(int formationPosition) {
		this.formationPosition = formationPosition;
	}

}
